package psp;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Locale;

class HandleFileArray {
	
	private String fileName = null;
	
	private static final int INVALID_OPTION = -1;
	private static final int WRITE_FILE_ARRAY = 0;
	private static final int READ_FILE_ARRAY = 1;
	
	private int option = INVALID_OPTION;
	
	private static final int K = 10; // The constant K is used to control the number of arrays of arrays in the file. It should be at least 10.
	
	private LinkedList<LinkedList<Float>> fileRecords = new LinkedList<LinkedList<Float>>();

	/**
	 * Check the action the user wants to perform and execute it
	 */
	void performAction() {
		this.readHandleArraysOption();
		this.performHandleArrayAction();
	}
		
	/**
	 * The user enters the option he wants to perform (write or read an number array file)
	 */
	private void readHandleArraysOption() {
		String line = null;
		BufferedReader is = null;
		try {
	      is = new BufferedReader(
	        new InputStreamReader(System.in));
	      
	      this.printOptions();
	      line = is.readLine();
	      this.setOption(INVALID_OPTION);
	      
	      while (this.getOption() == INVALID_OPTION) {
	    	  if (line.trim().equals("0") || line.trim().equals("1"))
	    		  this.setOption(Integer.parseInt(line.trim()));
	    	  
	    	  if (this.getOption() == INVALID_OPTION) {
	    		  System.err.println("Invalid Option!");
	    		  this.printOptions();
	    	  }
	      }

	    } catch (NumberFormatException ex) {
	    	System.err.println("Not a valid number: " + line);
	    }
	    catch (IOException e) {
	    	System.err.println("Unexpected IO ERROR: " + e);
		}
	}	

	/**
	 * It directs to the action of writing or reading a file of number arrays, according to the user option. 
	 */
	private void performHandleArrayAction() {
		switch (this.option) {
		case WRITE_FILE_ARRAY: 
			this.writeArrayFile();
			break;
			
		case READ_FILE_ARRAY:
			this.readArrayFile();
			break;
		}
	}
	
	/**
	 * Write a file whose lines will be records of arrays of numbers. These records will be entered by the user.
	 */
	private void writeArrayFile() {
		String line = null;
		BufferedReader is = null;
		try {
			is = new BufferedReader(
					new InputStreamReader(System.in));
			
			int numbersCount = -1;
			
			try {
				System.out.println("Enter the number of arrays you want to store in the file: ");
				line = is.readLine();
				numbersCount = Integer.parseInt(line);
				
				while (numbersCount <= 0 ) {
					System.out.println("The number should be greater than 0. " +
							" Enter the number of arrays you want to store in the file: ");
					line = is.readLine();
					numbersCount = Integer.parseInt(line);
				}
				
			} catch (NumberFormatException ex) {
		    	System.err.println("Not a valid number: " + line);
		    }
			
			
			int i = 0;
		
			while (i < numbersCount) {
				System.out.print("Please insert the array number ");
				System.out.print(i+1);
				System.out.print(": ");
				line = is.readLine();
				
				try {
					this.insertRecordInArray(line);
					i++;
				} catch (IncorrectNumberItemsException inie) {
					System.err.println(inie.getMessage());
					System.err.println("A record must be have " + K + " items.");
				}
			}
			
			this.writeRecordsInFile();
		}
	    catch (IOException e) {
	    	System.err.println("Unexpected IO ERROR: " + e);
		}
	}
	
	/**
	 * Insert a record with numbers as a "line" of the matrix
	 * @param numbersInString Numbers in a string format
	 * @throws IncorrectNumberItemsException it is thrown when the user includes less than the minimum or more than the maximum number in the record
	 */
	private void insertRecordInArray(String numbersInString) throws IncorrectNumberItemsException {
		String[] numbers = numbersInString.split(" ");
			
		if (numbers.length > K)
			throw new IncorrectNumberItemsException(IncorrectNumberItemsException.GREATER_THAN_MAXIMUM); 
		
		if (numbers.length < K)
			throw new IncorrectNumberItemsException(IncorrectNumberItemsException.LESS_THAN_MINIMUM);
		
		LinkedList<Float> record = new LinkedList<Float>(); // Variable used to store the numbers of one record
		try {
			for (int i = 0; i < numbers.length; i++)
				if (! numbers[i].trim().equals(""))
					record.add(Float.parseFloat(numbers[i].trim()));
		} catch (NumberFormatException ex) {
	    	System.err.println("There is an incorrect number in your record: " + ex.getMessage());
	    	System.exit(0);
		}
		
		this.fileRecords.add(record);
	}
	
	/**
	 * Iterate over the matrix (LinkedList of LinkedList) writing the number in the file 
	 */
	private void writeRecordsInFile() {
		try {
		
			// open the output file
			PrintWriter out = new PrintWriter(this.fileName);
			
			ListIterator<LinkedList<Float>> iteratorRecords = this.fileRecords.listIterator();
			while (iteratorRecords.hasNext()) {
				ListIterator<Float> iterator = iteratorRecords.next().listIterator();
				while (iterator.hasNext())
					out.printf(Locale.ENGLISH, "%.6g ", iterator.next());
				out.printf("\n");
			}
			
			System.out.println("File created with the number arrays you included!");
			out.close();
		}
		catch (IOException e)
		{
			System.out.println("IOException : " + e);
		}
	}

	/**
	 * Reads the lines of a file and display in the screen
	 */
	private void readArrayFile() {
		try {
			FileInputStream fin = new FileInputStream(this.fileName);
			DataInputStream din = new DataInputStream(fin);
			
			String line = null;
			
			while (( line = din.readLine()) != null)
				System.out.println(line.trim());
			
			din.close();
		} catch (FileNotFoundException ex) {
			System.err.println("The file entered is not valid!");
		} catch (IOException ex){
		    ex.printStackTrace();
	    }
	}
	
	/**
	 * Display in the screen the options the user can perform with the number array file
	 */
	private void printOptions() {
		System.out.println("Handle File Array Option. Enter: ");
		System.out.println("0 - Write new number array file,");
		System.out.println("1 - Read number array file");
	}
	
	/**
	 * Set file name that will be read or written
	 * @param value file name
	 */
	void setFileName(String value) {
		this.fileName = value;
	}
	
	/**
	 *  Set the option the user chose to perform
	 * @param value option to be set
	 */
	private void setOption(int value) {
		this.option = value;
	}
	
	/**
	 *  Get the option the user had set before
	 *  @return option stored in the class
	 */
	private int getOption() {
		return this.option;
	}
}